import json
from collections import namedtuple

from flask import current_app, g, request
from flask_httpauth import HTTPBasicAuth
from itsdangerous import TimestampSigner, SignatureExpired, BadSignature
from itsdangerous import URLSafeSerializer

from app.libs.error_code import AuthFailed, Forbidden
from app.libs.scope import is_in_scope

auth = HTTPBasicAuth()
User = namedtuple('User', ['uid', 'ac_type', 'scope'])


# @auth.login_required和@auth.verify_password是成对使用，被login_required装饰器标识的会进入到verify_password验证方法

@auth.verify_password
def verify_password(username, password):   #一般验证username 和password，但是这里把token作为username传递进来
    # 参数传递方式如下：放到header中
        # key=Authorization
        # value = basic base64(username:password)
    user_info = validate_token(username)
    if not user_info:
        return False
    else: # g变量类似于request全局变量
        g.user = user_info  # 如果验证成功，把user_info放到g对象中，后面会用到
        return True



# 验证token
def validate_token(token):
    url_s = URLSafeSerializer(current_app.config['SECRET_KEY'])
    ts = TimestampSigner(current_app.config['SECRET_KEY'])

    try:
        token = url_s.loads(token,max_age=current_app.config['TOKEN_EXPIRATION'])
        print("解密的token", token)  # {"uid": 1, "type": 100, "scope": null}.ZsNxuw.aVkSeAHAppkvKiRrLLNvxqDNn08
        b_user_info = ts.unsign(token)  # byte类型 b{"uid": 1, "type": 100, "scope": null}
        user_info = json.loads(b_user_info) # {"uid": 1, "type": 100, "scope": null}
        print("1s内 用validate验证是否有效", ts.validate(token,max_age=1))
    except SignatureExpired as e:
        print('token过期',e)
        raise AuthFailed(msg='token过期', error_code=1003)
    except BadSignature as e:
        print('错误的签名',e)
        raise AuthFailed(msg='错误的签名',error_code=1003)

    uid = user_info['uid']
    ac_type = user_info['type']
    scope = user_info['scope']  # scope本来是取值是 1、2 在 app/models/user.py的verify方法中做了转换 ，所以对应的值可能是： AdminScope、UserScope， 在

    # 在这里判断如果 没有视图权限，则抛出异常
    is_allow = is_in_scope(scope, request.endpoint)
    if not is_allow:
        raise Forbidden(msg='没有权限')


    # 如果验证成功返回的对应 namedtuple 类型的user对象,  使用namedtuple对象的原因是因为他取值方便如 g.user.uid，如果使用dict取值则是 g.uer['uid']
    return User(uid, ac_type, scope)
